Client-Adapting APIs

Learn how to identify the client type and make the API response flexible.

Introduction#

In the real world, an API endpoint gets called by numerous types of end devices (such as mobile phones, laptops, tablets, and so on). Depending on the type of end device or situation, API responses can be flexible. For example:

  • A YouTube video's resolution provided by the server is different for a mobile device compared to a desktop browser. This can be due to the width of the screen, transfer protocol, and so on.

  • Instagram stories don’t have music for some regions. After identifying the region, the system may decide to add or remove music for users.

Therefore, an API generating the same response for every client request may not be suitable for all end devices. In essence, a flexible API response refers to the flexibility in terms of the response generated and resources provided by servers to clients—mainly depending on the user and client (such as its type, version, and so on).

To generate a flexible response, we need to provide flexibility on the client- or server-side. We’ll discuss different ways to make the response flexible using various examples in this lesson.

Approaches#

Adaptive responses can be achieved in the following ways:

Provide flexibility at multiple places to generate a flexible response
Provide flexibility at multiple places to generate a flexible response

Client-side flexibility#

Client-side flexibility makes the responses flexible on the client-side before displaying the results on the screen. We get the same response for all user scenarios or different client types, and the client tailors this response according to the requirements. All the clients receive the same number of fixed fields as a response from the server.

For example, Educative’s Careers page uses four images for small devices and eight images for large devices, as depicted in the following illustrations:

 Four images for small devices
Four images for small devices
 Eight images for large devices
Eight images for large devices

Although the response from the server remains the same (eight images are received for both device types), the frontend displays four images to make it look more suitable for the device’s smaller screen. Generally, if we use a large number of fixed fields in the response, then some of the fields might not be necessary for some clients. On the other hand, if we use very few fields in the response, these fields might not fulfill the client's requirements. Although the number of fields can be changed over time, it remains the same for all the clients.

This approach works fine in some cases, but it doesn't provide flexibility for varied user scenarios or client types. Next, we’ll discuss server-side flexibility for getting a tailored response from the server depending on the different user scenarios and clients.

Server-side flexibility#

Server-side flexibility makes the responses flexible on the server-side based on the client or user scenarios. We discuss the three approaches for achieving flexibility on the server-side, as given below:

  • Intermediate adaption code

  • Expanded fields

  • GraphQL

Intermediate adaption code (IAC) is a technique used to obtain server-side flexibility. The adaption code acts as an intermediate layer between the client and the API. Apart from determining the client type, this code is responsible for either forwarding the client's request to the relevant API gateway or tailoring the response based on the client type.

Client identification

Identification of the client is important because it helps the API gateway decide which downstream services to call. We describe the fields of the HTTP request header below in order to get the client information. Client identification is mainly done through the HTTP headers. Generally, two types of HTTP headers serve this purpose.

  • HTTP User-Agent
  • HTTP client hints (Accept-CH)

HTTP User-Agent

The HTTP User-Agent header is a string that allows servers to identify the client, OS, vendor, and version. This header is defined in the following way:

User-Agent<product> / <product-version> <comment> 
  • <product>: Identifies the name of the client, such as Mozilla.
  • <product-version>: Provides the version of the client.
  • <comment>: This is optional, we can use it to add details related to the operating system, and more.

For example, the macOS User-Agent string looks like this:

Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) 
AppleWebKit/605.1.15 (KHTML, like Gecko) 
Safari/605.1.15

As we can see, the string above is long and unstructured, which makes it difficult to parse from a developer’s perspective. Also, sharing detailed information about the client in the HTTP request creates privacy issues, because we need to prevent users from covert tracking (such as digital fingerprints). To overcome these issues, HTTP introduced client hints.

HTTP client hints

HTTP client hints are used when a server might want specific information about the client for subsequent requests. In response to the initial request, the server passes the HTTP header Accept-CH to inform the client about the fields the server is interested in, as depicted in the following illustration:

image

The server indicates the client by sending the Accept-CH header in the response. For example, the Accept-CH header may contain the following information:

Accept-CHRTT, Width, Sec-CH-UA-Platform

The client then sends information to the server in the subsequent requests. This information includes:

  • RTT: Round trip time
  • Width: The device’s width
  • Sec-CH-UA-Platform: A platform such as Windows, Linux, or macOS

We have a practical example of Netflix that uses the intermediate adaption code and provides unique end-points for 800+ devices. Let's discuss how they use this approach to handle different clients.

Netflix: A use case of IAC

According to Netflix, the one-size-fits-all (OSFA) REST API approach has serious limitations. The OSFA API treats all devices as the same and generates the same response regardless of the device type, which is also the same as client-side flexibility. Therefore, Netflix decided to use the IAC approach and customize its API because every client has specific properties, such as its processing power, screen real estate issue, and whether or not chunking is required for the content delivery, and so on.

Let's take a look at the following illustration to observe how Netflix works before and after adding the IAC to the architecture.

Created with Fabric.js 3.6.6
Opening a Netflix page

1 of 3

Created with Fabric.js 3.6.6
OSFA API forwards requests

2 of 3

Created with Fabric.js 3.6.6
Client adaptation middleware's functionality

3 of 3

Each slide's functionality is explained below:

  • Slide 1: Let’s say a device opens a Netflix main page, and on the backend, multiple requests are sent to the OSFA API.

  • Slide 2: The OSFA API retrieves the data from the different services and formats and delivers it to the client-side.

  • Slide 3: The client adaptation middleware receives a single call to a unique URL (device-specific) that determines the client type, parses the request, and expands it to multiple requests. These requests are sent to the Java API, which further sends these requests to the corresponding services to get data. The Java API aggregates the response and sends it to the middleware. The client adaption middleware tailors the response according to the device’s properties and sends it back to the client.

To handle the different devices, Netflix uses the extra layer between the client-side code and the API where the client adaption code is written. This layer uses the customize endpoint for the specific device and tailors the data according to the client. Netflix uses different IACs for tablets and mobile phones to reflect device-to-device differences.

Quiz

Question

What are the two main responsibilities of IAC?

Hide Answer

Following are the two main responsibilities of IAC:

  • Client device identification: IAC identifies the client in various ways, such as using HTTP headers.
  • Forward the request to the appropriate API gateway to generate a flexible response or tailor the response based on the client type.

Expandable fields are used to request the expanded version of certain objects from an endpoint. Depending on the client type or any user-related scenario, we can request additional information through conditional parameters in the URL.

Let’s understand this by using the following hypothetical example of a restaurant finder application. There are two different devices accessing the same endpoint of this application. The devices exercise conditional parameters to request the following expanded objects.

  • Mobile phone: When the GET /finder/location endpoint is called via a mobile phone, the following response is obtained:
Without object expansion
  • Web browser: Next, we need to expand the images field for the web browser. The endpoint is GET /finder/location?expand=images, which gives the following response:

The API generates the response according to the expand parameter. Any client desiring to receive expanded objects will need to add the expand parameter depending on the type of end device or user-specific requirements. On the server-side, we achieve flexibility using the single endpoint.

GraphQL is another great technique for generating flexible API responses. Using this approach, the client defines their requirements as a query, and the API generates a flexible response according to the client's requirements. This approach requires the server to share a schema with the client beforehand. The schema defines the structure of available resources and the client's request format. Upon receiving the client's query, the API makes a single call to fetch the data from different services to meet the query's needs. Take the example above of the restaurant finder application. If we want to know additional information about the peak times to visit a restaurant, GraphQL is useful here. Instead of making a separate call to get the peak times, we send the query to the server in the following way:

The GraphQL query for getting the location and popular times of Seattle's restaurants

The following response is obtained from the query:

The response of the GraphQL query

We can conclude that by using GraphQL, we don’t need to send multiple calls to retrieve the required data from different APIs. GraphQL provides flexibility using a single request to receive data, such as location and popular timings, from different services.

The mechanisms for providing the flexibility we discussed in this lesson are not exhaustive, but it gives some idea of how we can make an API response adaptive.

Why make API responses flexible?#

In the sections above, we discussed some approaches to achieving flexibility in API responses. However, making API responses flexible may not always be a fruitful effort. This section discusses the advantages and disadvantages of achieving flexibility in API responses.

Advantages #

  • Improved design of the application is achieved by choosing the optimal end (client or server) to perform the processing.

  • Enhanced performance of the overall application is achieved, which leads to a better user experience.

  • May result in the efficient usage of bandwidth and other client- or server-side resources (memory, processing power, and so on) because only the required data is communicated between the two ends.

Drawbacks#

  • Adding flexibility to API responses leads to added complexity. For example, an IAC layer requires extensive and optimized coding solutions that should handle all types of devices or different user scenarios.

  • Achieving flexibility in API responses introduces an additional financial cost for the service provider. For example, an additional number of endpoints management will multiply the expense.

Quiz

Q

Let’s consider an application that provides chat services for almost every size and version of the device. We also want to add less load on the client-side, and size or version compatibility should be handled on the server-side. What would be a good approach to handle this scenario?

Your Answer
A)

Client-side flexibility

Correct Answer
B)

Server-side flexibility

Explanation

IAC helps us to identify the client type and its version, and forwards the request to the appropriate API gateway to retrieve the customized data according to the client’s requirements.

Summary#

In this lesson, we saw how we could tailor API responses as per a specific use case or client device. Often, our primary goal is to provide a client with a better experience and efficiently utilize computational resources.

Rate Limiting

Data Fetching Patterns